﻿// RaytracingScene.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "BaseInc.h"
#include <iostream>
#include "Ray.h"
#include "Camera.h"
#include "Framebuffer.h"
#include "SceneObject.h"
#include "math.h"
#include "RaytracingScene.h"

#define RandFloat(a,b) (float)Rand(a*10, b*10) / 10

//坐标系 z值向外越小
Ray RaytracingScene::AssginRay(int x, int y, Ray& ray)
{
    //当前光线的中心是400，300
    //vec3 pos = vec3(rand() % 6 - 1, rand() % 6 - 1, -300);
    
    vec3 pos = vec3(400 + RandFloat(0, 4) - 2., 300 + RandFloat(0, 4) - 2., -600);
    vec3 dir = vec3(x, y, 0) - pos;  //dir改成负的
    ray.pos = pos;//vec3(x, y, -500);// -dir * 5;
    ray.dir = normalize(dir);
    ray.depth = 4;
    ray.color = vec3(0);	// vec3(255);
    return ray;
}
void RaytracingScene::StartRenderThread(vec2 leftUp, vec2 rightDown)
{
    for (int y = leftUp.y; y < rightDown.y; y++)
    {
        for (int x = leftUp.x; x < rightDown.x; x++)
        {
            RenderOne(nSpp, x, y);
        }
    }
}
void RaytracingScene::MultithreadRun(int numthread)
{
    vector<bool> vctRenderFinish(numthread);
    float hei = (float)frame.height / (float)numthread;
    for (int i = 0; i < numthread; i++)
    {
        std::thread th0([=, &vctRenderFinish]() {
            StartRenderThread(vec2(0, hei * i), vec2(frame.width, (i + 1) * hei));
            vctRenderFinish[i] = true;
            });
        th0.detach();
    }

    bool bWaitFinished = true;
    int i = 0;
    while (bWaitFinished)
    {
        for (i = 0; i < numthread; i++)
        {
            if (!vctRenderFinish[i])
            {
                break;
            }
            else {
                cout << "render part" << i << "finished" << endl;
            }
        }
        if (i == numthread)
        {
            bWaitFinished = false;
        }
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    frame.SaveToFile("out.jpg");
    cout << "finished frame" << endl;
}
void RaytracingScene::RenderOne(int nSpp, int x, int y)
{
#ifndef  DEBUG
    Ray curRay;
    vec3 color;
    for (int i = 0; i < nSpp; i++)
    {
        AssginRay(x, y, curRay);
        Ray outRay;
        int count = scene.childs.size();    //遍历id
        for (int i = 0; i < count; i++)
        {
            std::shared_ptr<math::Geometry::GeometryObject> geo = scene.childs[i % scene.childs.size()];
            if (geo->Hit(curRay, outRay)->IsHit)
            {
                count += i;
                curRay.depth--;
                color += curRay.color;
                if (curRay.depth == 0)
                {
                    break;
                }
            }
        }
    }
    frame.SetColor(color / float(nSpp), vec2(x, y));
#else
    // for Texture GetColor and FrameGetColor test
    Texture* tex = TextureManager::GetInst().GetTexture(0);
    //  vec3 colorTmp = tex->GetTextureColor(x, y);
    vec3 colorTmp = tex->GetColor(vec2(x / 800.0, y / 600.0));

    frame.SetColor(colorTmp, vec2(x, y));
#endif // !DEBUG
}
void RaytracingScene::Run()
{
    for (int y = 0; y < frame.height; y++)
    {
        for (int x = 0; x < frame.width; x++)
        {
            RenderOne(nSpp, x, y);
        }
    }
    frame.SaveToFile("out.jpg");
}
void RaytracingScene::LoadFromJson(std::string& file)
{
    Json::Value root;
    Json::String err;
    int val1;
    string val2;
    char buff[1024];
    memset(buff, 0, 1024);

    ifstream ifs(file, std::ios::in);
    if (ifs.is_open())
    {
        Json::CharReaderBuilder builder;
        if (Json::parseFromStream(builder, ifs, &root, &err))
        {
            scene.name = root["name"].asCString();
            for (int i = 0; i < root["data"].size(); i++)
            {
                Value& v = root["data"];
                if (strcmp(v["type"].asCString(), v["model"].asCString()) == 0)
                {
                }
            }
        }


    }
    ifs.close();
}



glm::vec3 reflect3(const glm::vec3& lightDir, const glm::vec3& normal) {
    return lightDir - 2 * glm::dot(normal, lightDir) * normal;
}
int main()
{
    using namespace math::Geometry;
    using namespace math;
    using namespace chrono;

    int nSpp = 1;
    int numthread = 5;
    bool bMultithreadRendering = true;

    RaytracingScene scene;

    {
        cout << "分别输入 numThread nSpp" << endl;
        cin >> numthread >> nSpp;
        if (numthread > 1) bMultithreadRendering = true;
        if (nSpp > 0)
            scene.nSpp = nSpp;
    }

    auto tpBegin = steady_clock::now();
    if (bMultithreadRendering)
    {
        scene.MultithreadRun(numthread);  //16核心32线程
    }
    else {
        scene.Run();
    }
    auto tpEnd = steady_clock::now();
    steady_clock::duration dur = tpEnd - tpBegin;
    printf("render finished,numthread:%d,cost:%ds", bMultithreadRendering ? 1 : numthread, duration_cast<seconds>(dur));
    getchar();
    return 0;
}

